#include <time.h>
#include <iostream>
#include <stdexcept>
#include <iomanip>
#include "types/DateTime.h"

using namespace std;

DateTime::DateTime(){
    time_t rawtime;
    struct tm * timeinfo;
    time (&rawtime);
    timeinfo = localtime (&rawtime);
    double YY=timeinfo->tm_year+1900;
    double MM=timeinfo->tm_mon+1;
    double DD=timeinfo->tm_mday;
    double h=timeinfo->tm_hour;
    double m=timeinfo->tm_min;
    if (DD==31)
        DD=30;
    year=YY;
    month=MM;
    day=DD;
    hour=h;
    minute=m;
}
DateTime::DateTime(double YY, double MM, double DD, double h, double m){
	if (YY<0)
		throw std::out_of_range("Invalid Year");
	else
		if ((MM<1)||(MM>12))
			throw std::out_of_range("Invalid Month");
		else
			if ((DD<0)||(DD>30))
				throw std::out_of_range("Invalid Day");
			else
				if ((h<0)||(h>23))
					throw std::out_of_range("Invalid Hour");
				else
					if ((m<0)||(m>59))
						throw std::out_of_range("Invalid Minute");
	year=YY;
    month=MM;
    day=DD;
    hour=h;
    minute=m;
}
DateTime::DateTime(double YY, double MM, double DD){
	if (YY<0)
		throw std::out_of_range("Invalid Year");
	else
		if ((MM<1)||(MM>12))
			throw std::out_of_range("Invalid Month");
		else
			if ((DD<0)||(DD>30))
				throw std::out_of_range("Invalid Day");
    year=YY;
    month=MM;
    day=DD;
    hour=00;
    minute=00;
}
DateTime::DateTime(const DateTime &d){
    this->year=d.year;
    this->month=d.month;
    this->day=d.day;
    this->hour=d.hour;
    this->minute=d.minute;
}

void DateTime::setYear(double YY){
	if (YY<0)
		throw std::out_of_range("Invalid Year");
	year=YY;
}
void DateTime::setMonth(double MM){
	if ((MM<1)||(MM>12))
		throw std::out_of_range("Invalid Month");
	month=MM;
}
void DateTime::setDay(double DD){
	if ((DD<0)||(DD>30))
		throw std::out_of_range("Invalid Day");
	day=DD;
}
void DateTime::setHour(double h){
	if ((h<0)||(h>23))
		throw std::out_of_range("Invalid Hour");
	hour=h;
}
void DateTime::setMinute(double m){
	if ((m<0)||(m>59))
		throw std::out_of_range("Invalid Minute");
	minute=m;
}

double DateTime::getYear(void){
	return year;
}
double DateTime::getMonth(void){
	return month;
}
double DateTime::getDay(void){
	return day;
}
double DateTime::getHour(void){
	return hour;
}
double DateTime::getMinute(void){
	return minute;
}

DateTime& DateTime::operator = (const DateTime& Date){
	this->year=Date.year;
	this->month=Date.month;
	this->day=Date.day;
	this->hour=Date.hour;
	this->minute=Date.minute;
	return *this;
}
DateTime DateTime::operator + (const double d){
	if (d<0)
		throw std::out_of_range("Days to add < 0");
	double minutos=0;
	minutos+=this->year*518400;
	double mes=this->month;
	minutos+=(mes-1)*43200;
	double dia=this->day;
	minutos+=(dia-1)*1440;
	minutos+=this->hour*60;
	minutos+=this->minute;
	double sumar=d*1440;
	double total=minutos+sumar;
	double anos=int(total/518400);
	total-=anos*518400;
	double meses=0;
    meses+=int(total/43200);
	total-=meses*43200;
	if(total>0)
        meses++;
	double dias=0;
    dias+=int(total/1440);
	total-=dias*1440;
	if(total>0)
        dias++;
	double horas=int(total/60);
	total-=horas*60;
	DateTime resultado=DateTime(anos,meses,dias,horas,total);
	return resultado;
}
DateTime DateTime::operator - (const double d){
    if (d<0)
		throw std::out_of_range("Days to deduct < 0");
	double minutos=0;
	minutos+=this->year*518400;
	double mes=this->month;
	minutos+=(mes-1)*43200;
	double dia=this->day;
	minutos+=(dia-1)*1440;
	minutos+=this->hour*60;
	minutos+=this->minute;
	double restar=d*1440;
	if (restar>minutos)
		throw std::out_of_range("Days to deduct > total days");
	double total=minutos-restar;
	double anos=int(total/518400);
	total-=anos*518400;
	double meses=0;
    meses+=int(total/43200);
	total-=meses*43200;
	if(total>0)
        meses++;
	double dias=0;
    dias+=int(total/1440);
	total-=dias*1440;
	if(total>0)
        dias++;
	double horas=int(total/60);
	total-=horas*60;
	DateTime resultado=DateTime(anos,meses,dias,horas,total);
	return resultado;
}
double DateTime:: operator - (const DateTime d2){
    double minutos=0;
	minutos+=this->year*518400;
	double mes=this->month;
	minutos+=(mes-1)*43200;
	double dia=this->day;
	minutos+=(dia-1)*1440;
	minutos+=this->hour*60;
	minutos+=this->minute;
	double minutos2=0;
	minutos2+=d2.year*518400;
	double mes2=d2.month;
	minutos2+=(mes2-1)*43200;
	double dia2=d2.day;
	minutos2+=(dia2-1)*1440;
	minutos2+=d2.hour*60;
	minutos2+=d2.minute;
	if (minutos<minutos2)
        throw std::out_of_range("Date 1 < Date2");
    return (minutos-minutos2)/1440;
}

bool DateTime::operator == (const DateTime d){
	return ((this->year==d.year)&&(this->month==d.month)&&(this->day==d.day)&&(this->hour==d.hour)&&(this->minute==d.minute));
}
bool DateTime::operator!= (const DateTime d){
	return (!((this->year==d.year)&&(this->month==d.month)&&(this->day==d.day)&&(this->hour==d.hour)&&(this->minute==d.minute)));
}
bool DateTime::operator < (const DateTime d){
	bool menor=false;
	if (this->year<d.year)
		menor=true;
	else
		if (this->year==d.year)
			if (this->month<d.month)
				menor=true;
			else
				if (this->month==d.month)
					if (this->day<d.day)
						menor=true;
					else
						if (this->day==d.day)
							if (this->hour<d.hour)
								menor=true;
							else
								if (this->hour==d.hour)
									if (this->minute<d.minute)
										menor=true;
	return menor;
}
bool DateTime::operator > (const DateTime d){
	bool mayor=false;
	if (this->year>d.year)
		mayor=true;
	else
		if (this->year==d.year)
			if (this->month>d.month)
				mayor=true;
			else
				if (this->month==d.month)
					if (this->day>d.day)
						mayor=true;
					else
						if (this->day==d.day)
							if (this->hour>d.hour)
								mayor=true;
							else
								if (this->hour==d.hour)
									if (this->minute>d.minute)
										mayor=true;
	return mayor;
}
bool DateTime::operator <= (const DateTime d){
	bool menor=false;
	if (this->year<d.year)
		menor=true;
	else
		if (this->year==d.year)
			if (this->month<d.month)
				menor=true;
			else
				if (this->month==d.month)
					if (this->day<d.day)
						menor=true;
					else
						if (this->day==d.day)
							if (this->hour<d.hour)
								menor=true;
							else
								if (this->hour==d.hour)
									if ((this->minute<d.minute)||(this->minute==d.minute))
										menor=true;
	return menor;
}

bool DateTime::operator >= (const DateTime d){
	bool mayor=false;
	if (this->year>d.year)
		mayor=true;
	else
		if (this->year==d.year)
			if (this->month>d.month)
				mayor=true;
			else
				if (this->month==d.month)
					if (this->day>d.day)
						mayor=true;
					else
						if (this->day==d.day)
							if (this->hour>d.hour)
								mayor=true;
							else
								if (this->hour==d.hour)
									if ((this->minute>d.minute)||(this->minute==d.minute))
										mayor=true;
	return mayor;
}
//Destructor
DateTime::~DateTime(){

}
//Entrada y salida
ostream& operator << (ostream& output,DateTime d){
    output << setfill('0') << d.getYear() << "/" << setw(2) << d.getMonth() << "/" << setw(2) << d.getDay() << " " << setw(2) << d.getHour() << ":" << setw(2) << d.getMinute();
    return output;
}
